package com.sitech.ibnms.c3p;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sitech.ibnms.c3p.service.IgniteC3PScheduleServiceImpl;
import com.sitech.ibnms.c3p.service.IgniteC3pTaskFlowServiceImpl;
import com.sitech.ibnms.c3p.service.IgniteScriptExecutorImpl;
import com.sitech.ibnms.c3p.util.JSONUtil;
import com.sitech.ismp.common.server.SpringContext;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static org.apache.ignite.IgniteSystemProperties.IGNITE_NO_ASCII;

/**
 * 应用入口
 * Created by liujhc on 7/3.
 */
@Configuration//表示这个类为配置类，可实现xml功能(通过java方法，实现spring xml的bean注入)
@ComponentScan("com.sitech.ibnms.c3p")//目录下bean会自动扫描
@EnableAutoConfiguration //开启自动配置功能,springboot对spring进行封装，
@ImportResource("${application.spring.contextConfigLocations}") //导入别样配置，
public class Application {

    private static ApplicationContext applicationContext;
    private static final Lock lock = new ReentrantLock();
    private static final Condition initialized = lock.newCondition();

//生成多个bean
    @Bean
    public ObjectMapper objectMapper() {
        return JSONUtil.objectMapper();
    }

    public static void main(String[] args) {
        System.setProperty(IGNITE_NO_ASCII, "Y"); // 不输出Ignite的logo
        SpringApplication application = new SpringApplication(Application.class);
        application.setShowBanner(false);//启动时要不要显示依赖的日志
        application.run(args);
    }

    public static <T> T getBean(String name) {
        return (T) getContext().getBean(name);
    }

    public static <T> T getBean(Class<T> requiredType) {
        return getContext().getBean(requiredType);
    }

    public static ApplicationContext getContext() {
        if (applicationContext == null) {
            lock.lock();
            try {
                initialized.await(60, TimeUnit.SECONDS);
                Assert.notNull(applicationContext, "ApplicationContext is not initialized");
            } catch (InterruptedException e) {
                throw sneakyThrow(e);
            } finally {
                lock.unlock();
            }
        }
        return applicationContext;
    }

    @Autowired
    private void init(ApplicationContext ctx) {
        lock.lock();
        try {
            applicationContext = ctx;
            SpringContext.setBeanFactory(ctx);
            initialized.signalAll();
        } finally {
            lock.unlock();
        }
    }

    @Autowired
    public void deployIgniteServices(Ignite ignite) {//用于发布ignite服务
        // 部署ignite服务
        IgniteServices igniteServices = ignite.services(ignite.cluster().forServers());//整个集群中只有一个
        //通过这个对象提供单例服务，如果这个服务在这个节点挂了，会在其他节点启动，
        //每个节点都会有一个
        //Ignite提供服务，作为缓存
        //通过spring api将其存到Ignite
        //a节点put一个cache，在b节点也可以取到 如果没有则会实际查询
        igniteServices.deployNodeSingleton("C3pTaskFlowService", new IgniteC3pTaskFlowServiceImpl());
        igniteServices.deployNodeSingleton("C3pScheduleService", new IgniteC3PScheduleServiceImpl());
        igniteServices.deployNodeSingleton("ScriptExecutor", new IgniteScriptExecutorImpl());
    }

    /**
     * 用于躲避java编译器对非RuntimeException的检查，从而实现直接抛出原始异常对象
     *
     * @param throwable
     * @return
     */
    public static RuntimeException sneakyThrow(Throwable throwable) {
        Assert.notNull(throwable);
        Application.<RuntimeException>sneakyThrow0(throwable);
        return null;
    }

    private static <T extends Throwable> void sneakyThrow0(Throwable throwable) throws T {
        //noinspection unchecked
        throw (T) throwable;
    }
}
